// Vue原型添加_render函数 每个vm实例通过原型链可以访问到
export function renderMixin(Vue) {
  // 创建render函数里面的_c 标签 _v文本 _s变量这几个函数,通过这几个函数把render函数===>转成vnode虚拟dom
  Vue.prototype._c = function () {
    //标签
    return createElement(this, ...arguments); //传递参数
  };
  Vue.prototype._v = function (text) {
    // 文本
    return createText(this, text);
  };
  Vue.prototype._s = function (val) {
    // 变量_s(msg) typeof []  typeof {} 都是object 变成字符串
    console.log(val);
    return val == null
      ? ""
      : typeof val == "object"
        ? JSON.stringify(val)
        : val;
  };
  Vue.prototype._render = function () {
    // 将render函数 变成vnode

    let vm = this; //当前实例
    let render = vm.$options.render;
    let vnode = render.call(this); //执行render函数===>传递this到 new Function(`with(this){return ${code}}`)===> with(this) { //这里可以使用this}
    console.log(
      vnode,
      this,
      "返回--vnode---this是关键和传递this到 new Function(`with(this){return ${code}}`)===> with(this) { //这里可以使用this}",
    );
    return vnode; //得到把虚拟dom返回出去
  };
}
// _c('div',===>tag
//     {id:"app",style:{"color":" red","font-size":" 12px"}} ===> data,
//     _v("hello:"+_s(name)+"我是:"+_s(list)),_c('h1',
//     undefined
//     ,_v("我是子h1标签")) ===> children
//       )
//创建元素---拼接生成虚拟dom
function createElement(vm, tag, data = {}, ...children) {
  // 注意： 这里匹配到的有两种  一种 是html标签，一种是组件 所以需要分别进行处理 最终将标签或者组件变成vnode（虚拟dom）
  //
  if (isResved(tag)) {
    // html标签
    return vnode(vm, tag, data, data.key, children, undefined);
  } else {
    //组件  通过this  vm来获取组件信息
    const Ctor = vm.$options["components"][tag]; //获取到自己的组件信息
    // console.log(Ctor, vm.$options.components[tag], tag, '组件化--Ctor--处理组件标签')
    // 然后实现 将 组件信息 转换为虚拟dom
    return CreateComponent(vm, tag, data, data.key, children, Ctor);
  }

  // 声明将 组件 转换为虚拟dom的方法
  function CreateComponent(vm, tag, data, key, children, Ctor) {
    // Ctor就是我们声明的 Sub 构造函数
    console.log(
      vm,
      tag,
      data,
      key,
      children,
      Ctor,
      typeof Ctor,
      "组件化--先把标签转成vnode虚拟dom",
    );
    // 内部引入的自定义组件 是 对象  Vue.component注册的自定义组件 Ctor是函数
    if (typeof Ctor == "object") {
        // 内部组件
      Ctor = vm.constructor.extend(Ctor); //创建一个新的组件构造函数。以便后续可以实例化这个组件.组件 == 构造函数
    }

    // 添加一个方法 hooks 用来处理组件的钩子函数，当组件构造函数被 new实例的化的时候，组件初始化
    data.hook = {
      init(vnode) {
        // 子组件初始化---创建实例
 
        let childInstanch = (vnode.componentInstance =
          new vnode.componentOptions.Ctor({})); //new 子组件构造函数 创建子组件的实例，并且复制给一个新的变量放到vnode中
        console.log(childInstanch, "组件化--得到子组件的实例");
        childInstanch.$mount(); // $mount没有传递  默认会将组件挂载到$el上---这个时候就生成了 组件的 真实的dom
        console.log(vnode.componentOptions.Ctor, "组件化----------init");
      },
    };

    console.log(
      Ctor,
      vnode("vm", "vue-component" + tag, data, key, children, undefined, {
        Ctor,
        children,
      }),
      "组件化--组件转成最后的vnode虚拟dom",
    );
    return vnode("vm", "vue-component" + tag, data, key, children, undefined, {
      Ctor,
      children,
    });
  }
}

function isResved(tag) {
  return [
    "a",
    "p",
    "div",
    "input",
    "i",
    "span",
    "ul",
    "li",
    "b",
    "h",
    "button",
  ].includes(tag);
}

//创建文本---拼接生成虚拟dom
function createText(vm, text) {
  return vnode(vm, undefined, undefined, undefined, undefined, text);
}
//创建vnode---拼接生成虚拟dom
function vnode(vm, tag, data, key, children, text, componentOptions) {
  return {
    vm,
    tag,
    data,
    key,
    children,
    text,
    componentOptions,
  };
}
//vnode 就是 js对象来描述节点（只能是节点)
// ast语法树比虚拟dom功能更强大，可以描述节点 css 等
